package cn.lxb.netstat.collector;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lxb.netstat.collector.NetstatCollectorProperties.FileProps;
import cn.lxb.netstat.collector.NetstatCollectorProperties.Format;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;

/**
 * 先放在内存作为缓存,然后定时同步到文件.
 * 
 * @author TFB
 */
@Log4j2
@AllArgsConstructor
public class FileAddressRepository extends MemoryAddressRepository {

	NetstatCollectorProperties properties;
	Thread saveFileThread;
	ObjectMapper objectMapper = null;

	public FileAddressRepository(NetstatCollectorProperties properties) {
		this.properties = properties;
	}

	@PostConstruct
	public void init() throws IOException {
		FileProps fileProps = properties.persist.file;
		File file = new File(fileProps.path);
		boolean exists = file.exists();
		if (!createfileIfNotExists(file)) {
			throw new RuntimeException("创建或检查文件失败:" + fileProps.path);
		}
		
		if (fileProps.format == Format.YAML) {
			objectMapper = new ObjectMapper(new YAMLFactory());
		} else {
			objectMapper = new ObjectMapper(new JsonFactory());
		}

		if (exists) {
			if(fileProps.loadOnStart) {
				log.info("加载原数据文件");
				loadFromFile(file);
			} else {
				log.info("清空原数据文件内容");
				saveToFile(file);
			}
		}

		saveFileThread = new SaveFileThread(fileProps, file);
		saveFileThread.start();
		log.info("saveFileThread started");
	}

	/**
	 * 文件不存在则创建,存在则校验是否正确,否则抛出异常.
	 */
	public static boolean createfileIfNotExists(File file) throws IOException {
		if (!file.exists()) {
			log.info("创建netstat-agent数据文件:" + file.getPath());
			if (!file.createNewFile()) {
				log.error("创建netstat-agent数据文件失败,请检查路径或权限:" + file.getPath());
				return false;
			}
		} else {
			log.info("已存在数据文件:" + file.getPath());
			if (!file.canRead() && !file.canWrite()) {
				log.error("数据文件不可读或写:" + file.getPath());
				return false;
			}
		}
		return true;
	}

	private void loadFromFile(File file) throws IOException {
		String content = FileUtil.readString(file, StandardCharsets.UTF_8);
		if (CharSequenceUtil.isNotBlank(content)) {
			try {
				appToAddress = objectMapper.readValue(content.trim(), new TypeReference<Map<String, Set<String>>>() {
				});
			} catch (IOException e) {
				log.error("读取已有数据文件出错,请检查格式和内容:" + e.getMessage());
				throw e;
			}
		}
	}

	private void saveToFile(File file) throws IOException {
		String content = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(appToAddress);
		try (FileWriter writer = new FileWriter(file, false)) {
			writer.write(content);
		}
		log.info("同步完成, 文件:{}", file.getPath());
	}
	
	class SaveFileThread extends Thread {
		FileProps fileProps;
		File file;

		public SaveFileThread(FileProps fileProps, File file) {
			super();
			setDaemon(true);
			setName("saveFileThread");
			this.file = file;
			this.fileProps = fileProps;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(fileProps.writeDelay.toMillis());
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}

			while (true) {
				try {
					if (!changed.get()) {
						log.debug("内容无有修改,不同步");
					} else {
						log.info("内容有修改,开始同步...");
						saveToFile(file);
						changed.compareAndSet(true, false);
					}
					Thread.sleep(fileProps.writePeriod.toMillis());
				} catch (InterruptedException e) {
					e.printStackTrace();
					break;
				} catch (Exception e) {
					log.error("Exception:" + e.getMessage(), e);
				}
			}
		}
	}

}
